#include<sys/types.h>
#include<sys/socket.h>
#include<strings.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<signal.h>
#include<sys/wait.h>
#include<sys/time.h>
#include<pthread.h>

#define LISTEN_PORT 1234

//服务器处理
void str_echo(int sockfd)            // 服务器收到客户端的消息后的响应
{
    ssize_t n;
    char line[512];

    printf("ready to read/n");

    while( (n=read(sockfd,line,512))>0 )      //阻塞IO版本
    {
            line[n]='\0';
            printf("Client Diary: %s\n",line);

            char msgBack[512];
            snprintf(msgBack,sizeof(msgBack),"recv: %s\n",line);
            write(sockfd,msgBack,strlen(msgBack));
            bzero(&line,sizeof(line));
    }
    printf("end read\n");
}

//子进程结束的信号处理
float timeuse;
void sig_child(int signo)         //父进程对子进程结束的信号处理
{
    pid_t pid;
    int   stat;

    struct timeval tpstart,tpend;
    //float timeuse;
    gettimeofday(&tpstart,NULL);

    while( (pid=waitpid(-1,&stat,WNOHANG))>0)
    printf("child %d terminated\n",pid);

    gettimeofday(&tpend,NULL);

    timeuse+=1000000*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;
    //timeuse/=1000000;
    printf("Use Time:%f\n",timeuse/1000000);
    return;
}



//使用线程代替进程来处理客户请求
void* threadPerClient(void *arg)
{
    int connfd=*((int*)arg);
    free(arg);                //防止内存泄露
    pthread_detach(pthread_self());

    printf("client from %d(socket num)\n",connfd);
    str_echo(connfd);

    close( connfd );
    return NULL;
}


int main(int argc, char **argv)
{
    int listenfd, connfd;
    pid_t childpid;
    socklen_t chilen;

    struct sockaddr_in chiaddr,servaddr;

    //for select
    int i,maxi,maxfd,sockfd;
    int nready,client[FD_SETSIZE];
    ssize_t n;
    fd_set rset,allset;
    //for select

    listenfd=socket(AF_INET,SOCK_STREAM,0);
    if(listenfd==-1)
    {
        printf("socket established error: %s\n",(char*)strerror(errno));              
    }

    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
    servaddr.sin_port=htons(LISTEN_PORT);
printf("listen port %d\n", LISTEN_PORT);
    int bindc=bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
    if(bindc==-1)
    {
        printf("bind error: %s\n",strerror(errno));
    }

    listen(listenfd,SOMAXCONN);               //limit是SOMAXCONN

    //initial "select" elements
    maxfd=listenfd;            //新增listenfd，所以更新当前的最大fd
    maxi=-1;
    for(i=0;i<FD_SETSIZE;i++)
      client[i] = -1;
    FD_ZERO(&allset);
    FD_SET(listenfd,&allset);    
    //end initial
	struct timeval tv;

    int cliconn=0;
    signal(SIGCHLD,sig_child);
    for(;;)
    {
		FD_ZERO(&rset);
		FD_SET(listenfd, &rset);  // add fd
		// timeout setting
		tv.tv_sec = 1;
		tv.tv_usec = 0;

        rset=allset;
        nready=select(maxfd+1,&rset,NULL,NULL,&tv); //一开始select监听的是监听口
        //如果有timeout设置，那么每次select之前都要再重新设置一下timeout的值
        //因为select会修改timeout的值。
        if(FD_ISSET(listenfd,&rset))
        {
            chilen=sizeof(chiaddr);
            
            connfd=accept(listenfd,(struct sockaddr*)&chiaddr,&chilen);
            //阻塞在accept，直到三次握手成功了才返回
            if(connfd==-1)
            printf("accept client error: %s\n",strerror(errno));
            else        
            printf("client connected:%d\n",++cliconn);

            for(i=0;i<FD_SETSIZE;i++)
            {
                if (client[i]<0)
                {
                    client[i]=connfd;    //找一个最小的插进入
                    break;
                }
            }
            if(i==FD_SETSIZE)
            {
                printf("too many clients\n");
                exit(0);
            }
            FD_SET(connfd,&allset);   //新加入的描述符，还没判断是否可以或者写，所以后面使用rset而不是allset



            if(connfd>maxfd)
              maxfd=connfd;
            if(i>maxi)
              maxi=i;
            if(--nready<=0)
              continue;
        }
        
        //for thread
        int *lptr;
        pthread_t pid;
        //for thread

        for(i=0;i<=maxi;i++)
        {
            if( (sockfd=client[i]) <0)
            continue;
            if(FD_ISSET(sockfd,&rset))
            {
                //thread client
                lptr=(int*)malloc(sizeof(int));
                *lptr=sockfd;
                //pthread_t pid;
                
                int errerr=pthread_create(&pid,NULL,threadPerClient,lptr);
                if(errerr!=0)
                printf("err %s",strerror(errno));

                FD_CLR(sockfd,&allset);                                //短连接后关闭socket，服务器发不过来
                client[i]=-1;

                printf("can read : %d,%d,%d\n",i,sockfd,nready);
                if(--nready<=0)
                break;
                //thread client

            }
        }

    }
}